import { LazyAggretate, LazyConvert, LazyList, LazyNumber, printLazyPairList, printList } from "./types"
import { map, cons, zip, take } from "./common";
import { within, square, addPair, relative, range } from "./number"
import { improve, superr } from "./compute";


const eps = () => 0.001;
const a = () => 0;
const b = () => 1;


///////////////////////// integration [BASIC] /////////////////////////

const easyIntegrate =
    (f: LazyConvert<number, number>) =>
        (a: LazyNumber) =>
            (b: LazyNumber): LazyNumber =>
                () => ((f(a)() + f(b)()) * (b() - a()) / 2)

//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
console.log(
    easyIntegrate(square)(a)(b)()
)
//<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<


const integrate =
    (f: LazyConvert<number, number>) =>
        (a: LazyNumber) =>
            (b: LazyNumber): LazyList<number> => {
                const x = a();
                const y = b();
                const mid = () => ((x + y) / 2);
                const slop = easyIntegrate(f)(() => x)(() => y)()
                return cons
                    (() => slop)
                    (
                        () => map
                            (addPair)
                            (zip<number, number>
                                (integrate(f)(() => x)(mid))
                                (integrate(f)(mid)(() => y))
                            )()
                    )
            }


const integration =
    (eps: LazyNumber) =>
        (f: LazyConvert<number, number>) =>
            (a: LazyNumber) =>
                (b: LazyNumber) =>
                    within(eps)(integrate(f)(a)(b))


const relativeIntegration =
    (eps: LazyNumber) =>
        (f: LazyConvert<number, number>) =>
            (a: LazyNumber) =>
                (b: LazyNumber) =>
                    relative(eps)(integrate(f)(a)(b))



///////////////////////// integration [BETTER] /////////////////////////

const integ =
    (f: LazyConvert<number, number>) =>
        (a: LazyNumber) =>
            (b: LazyNumber) =>
                (fa: LazyNumber) =>
                    (fb: LazyNumber)
                        : LazyList<number> => {
                        const m = () => ((a() + b()) / 2);
                        const fm = f(m);

                        return cons
                            (() => ((fa() + fb()) * (b() - a()) / 2))
                            (
                                () => map(addPair)(
                                    zip<number, number>(integ(f)(a)(m)(fa)(fm))
                                        (integ(f)(m)(b)(fm)(fb))
                                )()
                            )
                    }


const betterIntegrate =
    (f: LazyConvert<number, number>) =>
        (a: LazyNumber) =>
            (b: LazyNumber) =>
                integ(f)(a)(b)(f(a))(f(b));


const betterIntegration =
    (eps: LazyNumber) =>
        (f: LazyConvert<number, number>) =>
            (a: LazyNumber) =>
                (b: LazyNumber) =>
                    within(eps)(betterIntegrate(f)(a)(b))


const betterRelativeIntegration =
    (eps: LazyNumber) =>
        (f: LazyConvert<number, number>) =>
            (a: LazyNumber) =>
                (b: LazyNumber) =>
                    relative(eps)(betterIntegrate(f)(a)(b))



///////////////////////// TEST /////////////////////////


console.log(
    `integration: `,
    integration(eps)(square)(a)(b)()
)

console.log(
    `relativeIntegration: `,
    relativeIntegration(eps)(square)(a)(b)()
)

console.log(
    `betterIntegration: `,
    betterIntegration(eps)(square)(a)(b)()
)

console.log(
    `betterRelativeIntegration: `,
    betterRelativeIntegration(eps)(square)(a)(b)()
)
